package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.service.CartInfoAsyncService;
import com.atguigu.gmall.cart.service.CartInfoService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 购物车表 用户登录系统时更新冗余(CartInfo)表服务实现类
 *
 * @author makejava
 * @since 2021-04-16 19:01:26
 */
@Service("cartInfoService")
public class CartInfoServiceImpl implements CartInfoService {

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private CartInfoAsyncService cartInfoAsyncService;

    @Resource
    private ProductFeignClient productFeignClient;

    @Override
    public void addCart(Long skuId, Integer skuNum) {

        //真实用户或临时用户ID
        String userId = getUserId();

        //String userId = "3";
        //1:写Redis  同步  五大数据类型   String类型 值类型   token  userId

        // Hash类型  散列类型
        // 参数1： h    Map h = new HashMap();         用户ID   user:3:cart
        // 参数2： hk   h.put(k,                       skuId
        // 参数3： hv   h.put(k,v)                     CartInfo
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        String cacheHk = skuId.toString();
        //1.1 查询redis还是DB
        CartInfo cartInfo = (CartInfo) redisTemplate.opsForHash().get(cacheH, cacheHk);
        if (cartInfo != null) {
            //追加数量
            cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);
            //选中
            cartInfo.setIsChecked(CartInfo.ISCHECKED);
            //2:Mysql 异步 保存信息
            cartInfoAsyncService.updateByIdAsync(cartInfo);
        } else {
            //直接作为新商品 添加  Long skuId, Integer skuNum
            cartInfo = new CartInfo();
            //商品ID
            cartInfo.setSkuId(skuId);
            //数量
            cartInfo.setSkuNum(skuNum);
            //库存信息对象
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            cartInfo.setUserId(userId);
            //购物车价格
            cartInfo.setCartPrice(skuInfo.getPrice());
            //图片
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            //名称
            cartInfo.setSkuName(skuInfo.getSkuName());
            //实时价格
            cartInfo.setSkuPrice(skuInfo.getPrice());
            //2:Mysql  异步  保存购物车信息 （备份）
            cartInfoAsyncService.insertAsync(cartInfo);//子线程
        }
        //保存缓存中
        redisTemplate.opsForHash().put(cacheH, cacheHk, cartInfo);
        //设置过期时间  本次不设置了 算了
    }

    //查询购物车
    @Override
    public CartInfo toCart(Long skuId) {
        String userId = getUserId();
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        String cacheHk = skuId.toString();

        return (CartInfo) redisTemplate.opsForHash().get(cacheH, cacheHk);
    }

    @Override
    public List<CartInfo> cartList(String userId, String userTempId) {
        //
        if (!StringUtils.isEmpty(userId)) {
            //有真
            if (!StringUtils.isEmpty(userTempId)) {
                //有真 有临时  合并购物车返回
                return mergeCartList(userId, userTempId);
            } else {
                //有真 无临时  返回真购物车
                return getCartList(userId);
            }
        } else {
            //无真
            if (!StringUtils.isEmpty(userTempId)) {
                //无真 有临时  返回临时购物车
                return getCartList(userTempId);
            }
        }
        //啥也没有
        return null;
    }
    //复选框
    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        //真实用户或临时用户ID
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取真实用户ID
        String userId = AuthContextHolder.getUserId(request);
        if (StringUtils.isEmpty(userId)){
            userId = AuthContextHolder.getUserTempId(request);
        }
        //同步更新缓存
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        String cacheHk = skuId.toString();
        CartInfo cartInfo = (CartInfo) redisTemplate.opsForHash().get(cacheH, cacheHk);
        cartInfo.setIsChecked(isChecked);
        redisTemplate.opsForHash().put(cacheH,cacheHk,cartInfo);
        //异步更新DB
        cartInfoAsyncService.updateByIdAsync(cartInfo);
    }
    //商品清单
    @Override
    public List<CartInfo> getCartListChecked(String userId) {
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cacheH);
        //实时价格  如果加了Canal就没有必须写实时价格
        return cartInfoList.stream().filter(cartInfo -> cartInfo.getIsChecked() == 1).collect(Collectors.toList());
    }

    //合并购物车集合
    private List<CartInfo> mergeCartList(String userId, String userTempId) {
        //1.获取真实用户购物车集合
        List<CartInfo> cartListUserId = getCartList(userId);
        //2.获取临时用户购物车集合
        List<CartInfo> cartListUserTempId = getCartList(userTempId);
        //3.判断
        if (!CollectionUtils.isEmpty(cartListUserId)) {
            //真购物车
            if (!CollectionUtils.isEmpty(cartListUserId)) {
                //真购物车 + 临时购物车
                // Map  K skuId  V:CartInfo
                Map<Long, CartInfo> cartInfoUserIdMap = cartListUserId.stream().collect(Collectors.
                        toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
                cartListUserTempId.stream().forEach(cartInfoUserTemp -> {
                    //1、判断 临时购物车 是否在真实购物车集合中已经存在
                    //对临时购物车里的商品进行遍历，取出一件商品id去真实用户的购物车map里面进行寻找
                    //从map里取出对象  是吧map中原对象的地址赋给cartInfoUser  对cartInfoUser进行修改 就是对map里的原对象进项修改
                    CartInfo cartInfoUser = cartInfoUserIdMap.get(cartInfoUserTemp.getSkuId());
                    if(null != cartInfoUser){
                    //存在  追加数量 设置默认选中  真和临有相同的商品
                        cartInfoUser.setSkuNum(cartInfoUser.getSkuNum() +
                                cartInfoUserTemp.getSkuNum());
                        cartInfoUser.setIsChecked(CartInfo.ISCHECKED);
                        //更新redis
                        String cacheHUserId = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
                        redisTemplate.opsForHash().put(cacheHUserId,
                                cartInfoUser.getSkuId().toString(),cartInfoUser);
                        //异步更新DB
                        cartInfoAsyncService.updateByIdAsync(cartInfoUser);
                    }else {
                        //1.2 不存在 添加购物车  临时里有  真里没有

                        //添加redis
                        cartInfoUserTemp.setUserId(userId);
                        String cacheHUserId = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
                        redisTemplate.opsForHash().put(cacheHUserId,
                                cartInfoUserTemp.getSkuId().toString(),cartInfoUserTemp);
                        //异步添加DB
                        cartInfoAsyncService.insertAsync(cartInfoUserTemp);

                        cartInfoUserIdMap.put(cartInfoUserTemp.getSkuId(),cartInfoUserTemp);
                    }
                });
                //DB中删除临时
                cartInfoAsyncService.deleteByUserTempId(userTempId);
                //缓存中删除临时
                String cacheHUserTempId = RedisConst.USER_KEY_PREFIX + userTempId +
                        RedisConst.USER_CART_KEY_SUFFIX;
                redisTemplate.delete(cacheHUserTempId);
                //  将Map转回List集合
                return new ArrayList(cartInfoUserIdMap.values());
            } else {
                //有真购物车  无临时购物车
                return cartListUserId;
            }
        } else {
            //无真购物车
            if (!CollectionUtils.isEmpty(cartListUserId)) {
                //有临时购物车  无真购物车
                //因为真实用户已经存在了 已经登录了 更新缓存（主）   及 更新DB（子线程）（异步）
                String cacheHUserTempId = RedisConst.USER_KEY_PREFIX + userTempId +
                        RedisConst.USER_CART_KEY_SUFFIX;
                String cacheHUserId = RedisConst.USER_KEY_PREFIX + userId +
                        RedisConst.USER_CART_KEY_SUFFIX;
                Map<String, CartInfo> cartInfoTempIdMap = cartListUserTempId.stream().map(cartInfo -> {
                    //临时用户id转成真实用户id
                    cartInfo.setUserId(userId);
                    return cartInfo;
                }).collect(Collectors.toMap(
                        cartInfo -> cartInfo.getSkuId().toString(),
                        cartInfo -> cartInfo));//key为真实id
                //添加真实用户的缓存
                redisTemplate.opsForHash().putAll(cacheHUserId, cartInfoTempIdMap);
                //将临时用户的缓存删掉
                redisTemplate.delete(cacheHUserTempId);
                //开启子线程 更新DB
                cartInfoAsyncService.updateByIdAsyncExt(userTempId, userId);
                return cartListUserTempId;
            }
        }
        //啥也没有
        return null;
    }

    //根据用户id获取购物车机和
    private List<CartInfo> getCartList(String userId) {
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        //有可能更新实时价格
        return redisTemplate.opsForHash().values(cacheH);
    }

    private String getUserId() {
        //真实用户或临时用户ID
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //获取真实用户id
        String userId = AuthContextHolder.getUserId(request);
        if (StringUtils.isEmpty(userId)) {
            //临时用户
            userId = AuthContextHolder.getUserTempId(request);
        }
        return userId;
    }
}

